﻿using System;
using NTypeClasses;

namespace TypeClassSample.Math
{
	public struct Vector3D<T> : IEquatable<Vector3D<T>>
		where T : IEquatable<T>
	{
		public Vector3D(T x, T y, T z)
		{
			this.X = x;
			this.Y = y;
			this.Z = z;
		}

		static readonly Field<T> typeClass = TypeClass<Field<T>>.Instance;

		public readonly T X, Y, Z;

		public static Vector3D<T> operator +(Vector3D<T> a, Vector3D<T> b)
		{
			return new Vector3D<T>(
				x: typeClass.op_Addition(a.X, b.X),
				y: typeClass.op_Addition(a.Y, b.Y),
				z: typeClass.op_Addition(a.Z, b.Z)
			);
		}

		public static Vector3D<T> operator -(Vector3D<T> a, Vector3D<T> b)
		{
			return new Vector3D<T>(
				x: typeClass.op_Subtraction(a.X, b.X),
				y: typeClass.op_Subtraction(a.Y, b.Y),
				z: typeClass.op_Subtraction(a.Z, b.Z)
			);
		}

		public static Vector3D<T> operator *(Vector3D<T> v, T m)
		{
			return new Vector3D<T>(
				x: typeClass.op_Multiply(v.X, m),
				y: typeClass.op_Multiply(v.Y, m),
				z: typeClass.op_Multiply(v.Z, m)
			);
		}

		public static Vector3D<T> Zero
		{
			get { return new Vector3D<T>(x: typeClass.Zero, y: typeClass.Zero, z: typeClass.Zero); }
		}

		public static Vector3D<T> operator -(Vector3D<T> v)
		{
			return new Vector3D<T>(
				x: typeClass.op_UnaryNegation(v.X),
				y: typeClass.op_UnaryNegation(v.Y),
				z: typeClass.op_UnaryNegation(v.Z)
			);
		}

		public static Vector3D<T> operator +(Vector3D<T> v)
		{
			return new Vector3D<T>(
				x: typeClass.op_UnaryPlus(v.X),
				y: typeClass.op_UnaryPlus(v.Y),
				z: typeClass.op_UnaryPlus(v.Z)
			);
		}

		public static bool operator ==(Vector3D<T> a, Vector3D<T> b)
		{
			return typeClass.op_Equality(a.X, b.X) && typeClass.op_Equality(a.Y, b.Y) && typeClass.op_Equality(a.Z, b.Z);
		}

		public static bool operator !=(Vector3D<T> a, Vector3D<T> b)
		{
			return typeClass.op_Inequality(a.X, b.X) && typeClass.op_Inequality(a.Y, b.Y) && typeClass.op_Inequality(a.Z, b.Z);
		}

		public bool Equals(Vector3D<T> other)
		{
			return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
		}

		public override bool Equals(object obj)
		{
			if (obj == null || obj.GetType() != typeof(Vector3D<T>))
				return false;
			var other = (Vector3D<T>)obj;
			return Equals(other);
		}

		public override string ToString()
		{
			return X + "; " + Y + "; " + Z;
		}

		public override int GetHashCode()
		{
			return X.GetHashCode() ^ Y.GetHashCode() + Z.GetHashCode();
		}
	}
}
